package com.wanghua.spring.test.xml;

import com.wanghua.spring.util.ip.Test;
import org.dom4j.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.io.*;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.FileWriter;
import java.util.Iterator;
import java.util.List;

/**
 *Dom4j是一个易用的、开源的库，用于XML，XPath和XSLT。它应用于Java平台，采用了Java集合框架并完全支持DOM，SAX和JAXP。
 *
 * Element、Text类型节点对象都继承自 Node。
*/
public class Dom4jDemo {
    public static void main(String[] args) {
        String userDir = System.getProperty("user.dir"); //D:\技术区\Java\Spring\Spring
        String filename = userDir + "/src/com/wanghua/spring/test/xml/article.xml";
        File xmlFile = new File(filename);

        //１.读取并解析XML文档：读写XML文档主要依赖于org.dom4j.io包，其中提供DOMReader和SAXReader两类不同方式，而调用方式是一样的。这就是依靠接口的好处。
        //DOMReader reader = new DOMReader();
        SAXReader reader = new SAXReader();

        try {
            Document document = reader.read(xmlFile);  //读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题，注意要把各处的编码名称保持一致即可
            Element root = document.getRootElement();
            System.out.println("根元素：" + root.getName());

            //遍历XML树。DOM4J提供至少3种遍历节点的方法：
            //1) 枚举(Iterator)
            // 枚举根元素的所有子节点（仅第一层的子元素）
            for (Iterator i = root.elementIterator(); i.hasNext(); ) {
                Element article = (Element) i.next();
                String name = article.getName();  //article 节点
                System.out.println("   子元素：" + name);

                for (Iterator j = article.elementIterator(); j.hasNext(); ) {
                    Element elem = (Element) j.next();
                    name = elem.getName();
                    System.out.println("      子元素：" + name);
                }
            }
            System.out.println("---------------------------------------");

            // 枚举名称为 article 的节点
            for (Iterator i = root.elementIterator("article"); i.hasNext(); ) {
                Element article = (Element) i.next();
                String name = article.getName();
                String category = article.attribute("category").getValue();
                System.out.println("元素：" + name + "，类型：" + category);
            }
            System.out.println("---------------------------------------");

            // 枚举 root 元素属性
            for (Iterator i = root.attributeIterator(); i.hasNext(); ) {
                String rootName = root.getName();
                Attribute attribute = (Attribute) i.next();
                String attrName = attribute.getName();
                String attrValue = attribute.getValue();
                System.out.println("元素：" + rootName + "，属性" + attrName + "：" + attrValue);
            }
            System.out.println("---------------------------------------");

            //2) 递归
            //递归也可以采用Iterator作为枚举手段，但文档中提供了另外的做法
            treeWalk(root, "");
            System.out.println("---------------------------------------");

            //3) Visitor模式
            //Visitor 类似SAX的事件处理方式。Visitor接口提供多种Visit()的重载，根据XML不同的对象，将采用不同的方式来访问。
            root.accept(new MyVisitor()); //遍历 root 树。

            //4)  XPath支持
            // Dom4j可以使用Xpath来定位元素，需要jaxen包。下载地址：http://jaxen.codehaus.org/
            List list = document.selectNodes("/articles/article");  //select 可能返回 Element 对象，也可能是 Text 对象。如果 xpath 是 ".../text()"，则返回 Text 对象。
            for (int i = 0; i < list.size(); i++) {
                Element element = (Element) list.get(i);
                String category = element.valueOf("@category");
            }
            Element element = (Element) document.selectSingleNode("/articles/article[@category='java']/title");
            String name = element.getName();
            String value = element.getText();

            //5)  字符串与XML的转换
            String text = document.asXML(); //XML转字符串
            document = DocumentHelper.parseText(text); //字符串转XML

            //6)  用XSLT转换XML
            //Document doc = styleDocument(document, "D:/test.xslt");

            //7)  创建XML
            Document createdDoc = DocumentHelper.createDocument();
            Element rootElement = createdDoc.addElement("root").addAttribute("desc", "测试").addText("\n  ");
            Element author1 = rootElement.addElement("author")
                    .addAttribute("a", "a")
                    .addAttribute("b", "b")
                    .addText("张三");
            rootElement.addText("\n  ");
            Element author2 = rootElement.addElement("author")
                    .addAttribute("c", "c")
                    .addAttribute("d", "d")
                    .addText("李四");
            rootElement.addText("\n");
            text = createdDoc.asXML(); //XML转字符串

            //8)  修改XML
            createdDoc.getRootElement().attribute("desc").setValue("测试(修改的)");
            createdDoc.selectSingleNode("/root/author[@c]").setText("李四(修改的)"); //获取有 c 属性的 author 节点并设置.

            //9)  文件输出
            //一个简单的输出方法是将一个Document或任何的Node通过write方法输出
            FileWriter out = new FileWriter("D:/a.xml");
            createdDoc.write(out);
            out.close();

            //如果你想改变输出的格式，比如美化输出或缩减格式，可以用XMLWriter类
            //不使用格式
            out = new FileWriter("D:/b.xml");
            XMLWriter writer = new XMLWriter(out);
            writer.write(createdDoc);
            writer.close();
            // 美化格式
            out = new FileWriter("D:/c.xml");
            OutputFormat format = OutputFormat.createPrettyPrint();
            format.setEncoding("UTF-8");
            writer = new XMLWriter(out, format);
            writer.write(createdDoc);
            writer.close();
            // 缩减格式
            out = new FileWriter("D:/d.xml");
            format = OutputFormat.createCompactFormat();
            writer = new XMLWriter(out, format);
            writer.write(createdDoc);
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void treeWalk(Element element, String blankSpace) {
        blankSpace += "   ";
        for (int i = 0, size = element.nodeCount(); i < size; i++) { //element.nodeCount() 会获取元素的子节点，但并不是子元素，所以子节点中已包含了子元素
            Node node = element.node(i);
            if (node instanceof Element) {  //如果子节点是个元素，则将 Node 转为 Element 对象。
                Element elem = (Element) node;
                System.out.println(blankSpace + elem.getName());
                treeWalk(elem, blankSpace);
            } else if (node instanceof Text) {
                String text = node.getText();
                if (text.charAt(0) != '\n')
                    System.out.println(blankSpace + text);
            }
        }
    }

    public static Document styleDocument(Document document, String stylesheet) throws Exception {
        // load the transformer using JAXP
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(stylesheet));
        // now lets style the given document
        DocumentSource source = new DocumentSource(document);
        DocumentResult result = new DocumentResult();
        transformer.transform(source, result);
        // return the transformed document
        Document transformedDoc = result.getDocument();
        return transformedDoc;
    }
}
